<?php
// +----------------------------------------------------------------------
// | Author: wyd 2025/10/18
// | DESC: php8 注解的简单封装
// +----------------------------------------------------------------------

declare (strict_types=1);


namespace My\core;

use ReflectionMethod;

class AnnotationReader
{
    //?AnnotationReader 规定$instance是AnnotationReader类型，？标识可以为空
    public static ?AnnotationReader $instance = null;

    private array $cache = []; //缓存,里面存class,method,property

    //单例模式
    public static function getInstance(): self
    {
        if (self::$instance == null) {
            self::$instance = new self();
        }
        return self::$instance;
    }


    /**
     * 获取类的所有注解
     * @param string $className
     * @return array
     * @throws \ReflectionException
     */
    public function getClassAnnotations(string $className): array
    {
        if (isset($this->cache['class'][$className])) {
            return $this->cache['class'][$className];
        }
        $reflection = new \ReflectionClass($className);   //目标类的发射对象
        $annotations = [];
        foreach ($reflection->getAttributes() as $attribute) {
            $annotations[] = [
                'name' => $attribute->getName(),
                'instance' => $attribute->newInstance(),    //自己的使用的注解对象
                'arguments' => $attribute->getArguments(),
                'reflection'=> $reflection                  //自己的反射对象
            ];
        }
        $this->cache['class'][$className] = $annotations;
        return $annotations;
    }


    /**
     * 获取类的指定注解
     * @param string $className
     * @param string $annotationClass
     * @return mixed|void
     * @throws \ReflectionException
     */
    public function getClassAnnotation(string $className, string $annotationClass)
    {
        $annotations = $this->getClassAnnotations($className);  //这里写有缓存
        foreach ($annotations as $annotation) {
            if ($annotation['name'] === $annotationClass || is_subclass_of($annotation['name'], $annotationClass)) {
                return $annotation['instance'];
            }
        }
    }


    /**
     * 获取方法的注解
     * @param string $className
     * @param string $methodName
     * @return array|mixed
     * @throws \ReflectionException
     */
    public function getMethodAnnotations(string $className, string $methodName)
    {
        $cacheKey = $className . '::' . $methodName;
        if (isset($this->cache['method'][$cacheKey])) {
            return $this->cache['method'][$cacheKey];
        }

        $reflection = new ReflectionMethod($className, $methodName);
        $annotations = [];
        foreach ($reflection->getAttributes() as $attribute) {
            $annotations[] = [
                'name' => $attribute->getName(),
                'instance' => $attribute->newInstance(),
                'arguments' => $attribute->getArguments(),
                'reflection'=> $reflection
            ];
        }
        $this->cache['method'][$cacheKey] = $annotations;
        return $annotations;
    }


    /**
     * 获取属性的注解
     * @param string $className
     * @param string $propertyName
     * @return array
     * @throws \ReflectionException
     */
    public function getPropertyAnnotations(string $className, string $propertyName): array
    {
        $cacheKey = $className . '::' . $propertyName;
        if (isset($this->cache['property'][$cacheKey])) {
            return $this->cache['property'][$cacheKey];
        }
        $reflection = new \ReflectionProperty($className, $propertyName);
        $annotations = [];
        foreach ($reflection->getAttributes() as $attribute) {
            $annotations[] = [
                'name' => $attribute->getName(),
                'instance' => $attribute->newInstance(),
                'arguments' => $attribute->getArguments(),
                'reflection'=> $reflection
            ];
        }
        $this->cache['property'][$cacheKey] = $annotations;
        return $annotations;
    }


    /**
     * 清空缓存
     * @return void
     */
    public function clearCache(): void
    {
        $this->cache = [];
    }
}